package com.luhuiguo.cms.client.presenter;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;
import com.google.inject.Inject;
import com.luhuiguo.cms.client.AppMessages;
import com.luhuiguo.cms.client.AppResources;
import com.luhuiguo.cms.shared.event.AddChannelEvent;
import com.luhuiguo.cms.shared.event.ChannelCreatedEvent;
import com.luhuiguo.cms.shared.event.ChannelCreatedEventHandler;
import com.luhuiguo.cms.shared.event.ChannelUpdatedEvent;
import com.luhuiguo.cms.shared.event.ChannelUpdatedEventHandler;
import com.luhuiguo.cms.shared.event.EditChannelCancelledEvent;
import com.luhuiguo.cms.shared.event.EditChannelCancelledEventHandler;
import com.luhuiguo.cms.shared.event.EditChannelEvent;
import com.luhuiguo.cms.shared.event.FlashEvent;
import com.luhuiguo.cms.shared.event.LoadArticlesEvent;
import com.luhuiguo.cms.shared.event.LoginEvent;
import com.luhuiguo.cms.shared.event.LoginEventHandler;
import com.luhuiguo.cms.shared.event.RefreshChannelTreeEvent;
import com.luhuiguo.cms.shared.event.RefreshChannelTreeEventHandler;
import com.luhuiguo.cms.shared.model.ChannelModel;
import com.luhuiguo.cms.shared.model.UserModel;
import com.luhuiguo.cms.shared.rpc.FetchChannels;
import com.luhuiguo.cms.shared.rpc.FetchChannelsResult;
import com.luhuiguo.cms.shared.rpc.RemoveChannel;
import com.luhuiguo.cms.shared.rpc.RemoveChannelResult;
import com.luhuiguo.gwt.dispatcher.client.DispatcherAsync;
import com.luhuiguo.gwt.presenter.client.EventBus;
import com.luhuiguo.gwt.presenter.client.widget.WidgetDisplay;
import com.luhuiguo.gwt.presenter.client.widget.WidgetPresenter;

public class ChannelsPresenter extends WidgetPresenter<ChannelsPresenter.Display> {

    public interface Display extends WidgetDisplay {

        HasClickHandlers getRefreshClick();

        HasClickHandlers getAddClick();

        HasClickHandlers getEditClick();

        HasClickHandlers getDeleteClick();

        Tree getTree();

        void deleteSelectedChannel();

        void bindTreeItems(List<TreeItem> treeList);

        void setAddEnabled(boolean enabled);

        void setEditEnabled(boolean enabled);

        void setDeleteEnabled(boolean enabled);

        void setLoading(boolean loading);

    }

    private DispatcherAsync dispatcher;

    private AppMessages messages;

    private AppResources resources;

    private TreeItem treeItem;

    private ChannelModel channel;

    private UserModel user;

    @Inject
    public ChannelsPresenter(Display display, EventBus eventBus, DispatcherAsync dispatcher, AppMessages messages,
            AppResources resources) {
        super(display, eventBus);
        this.dispatcher = dispatcher;
        this.messages = messages;
        this.resources = resources;
        GWT.log("ChannelsPresenter created");
    }

    @Override
    protected void onBind() {
        GWT.log("ChannelsPresenter onBind");
        registerHandler(eventBus.addHandler(LoginEvent.TYPE, new LoginEventHandler() {
            public void onLogin(LoginEvent event) {
                user = event.getUser();
                loadTreeItems();
            }

        }));

        registerHandler(eventBus.addHandler(RefreshChannelTreeEvent.TYPE, new RefreshChannelTreeEventHandler() {

            @Override
            public void onRefreshChannelTree(RefreshChannelTreeEvent event) {
                loadTreeItems();
            }

        }));

        registerHandler(eventBus.addHandler(EditChannelCancelledEvent.TYPE, new EditChannelCancelledEventHandler() {

            @Override
            public void onEditChannelCancelled(EditChannelCancelledEvent event) {
                display.getTree().setSelectedItem(treeItem);

            }

        }));

        registerHandler(eventBus.addHandler(ChannelCreatedEvent.TYPE, new ChannelCreatedEventHandler() {

            @Override
            public void onChannelCreated(ChannelCreatedEvent event) {
                TreeItem item = createChannelTreeItem(event.getChannel());
                event.getTreeItem().addItem(item);
                event.getTreeItem().setState(true);
                display.getTree().setSelectedItem(item);
                eventBus.fireEvent(new FlashEvent(messages.channelCreated(event.getChannel().getTitle()), 2000));
            }

        }));

        registerHandler(eventBus.addHandler(ChannelUpdatedEvent.TYPE, new ChannelUpdatedEventHandler() {

            @Override
            public void onChannelUpdated(ChannelUpdatedEvent event) {
                event.getTreeItem().setHTML(imageItemHTML(event.getChannel().getTitle(), resources.folder()));
                event.getTreeItem().setUserObject(event.getChannel());
                eventBus.fireEvent(new FlashEvent(messages.channelUpdated(event.getChannel().getTitle()), 2000));
                display.getTree().setSelectedItem(event.getTreeItem());
            }

        }));

        registerHandler(display.getTree().addSelectionHandler(new SelectionHandler<TreeItem>() {
            public void onSelection(SelectionEvent<TreeItem> event) {
                treeItem = event.getSelectedItem();
                channel = (ChannelModel) treeItem.getUserObject();
                eventBus.fireEvent(new LoadArticlesEvent(channel.getId()));
            }
        }));

        registerHandler(display.getTree().addSelectionHandler(new SelectionHandler<TreeItem>() {

            public void onSelection(SelectionEvent<TreeItem> event) {
                GWT.log("ChannelTree onSelection");
                treeItem = event.getSelectedItem();
                channel = (ChannelModel) treeItem.getUserObject();

                if (null == channel.getParent()) {
                    display.setAddEnabled(true);
                    display.setEditEnabled(true);
                    display.setDeleteEnabled(false);
                } else {
                    display.setAddEnabled(true);
                    display.setEditEnabled(true);
                    display.setDeleteEnabled(true);
                }
            }

        }));

        registerHandler(display.getRefreshClick().addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                eventBus.fireEvent(new RefreshChannelTreeEvent());
            }
        }));

        registerHandler(display.getAddClick().addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                treeItem = display.getTree().getSelectedItem();
                channel = (ChannelModel) treeItem.getUserObject();
                eventBus.fireEvent(new AddChannelEvent(treeItem));
            }
        }));

        registerHandler(display.getEditClick().addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                treeItem = display.getTree().getSelectedItem();
                channel = (ChannelModel) treeItem.getUserObject();
                eventBus.fireEvent(new EditChannelEvent(treeItem));
            }
        }));

        registerHandler(display.getDeleteClick().addClickHandler(new ClickHandler() {
            public void onClick(ClickEvent event) {
                removeChannel();
            }

        }));

    }

    @Override
    protected void onRevealDisplay() {
        GWT.log("ChannelsPresenter onRevealDisplay");

    }

    @Override
    protected void onUnbind() {
        GWT.log("ChannelsPresenter onUnbind");

    }

    private void removeChannel() {
        display.setLoading(true);
        treeItem = display.getTree().getSelectedItem();
        channel = (ChannelModel) treeItem.getUserObject();

        if (Window.confirm(messages.confirmDeleteChannel(treeItem.getText()))) {

            dispatcher.execute(new RemoveChannel(channel.getId()), new AsyncCallback<RemoveChannelResult>() {

                public void onFailure(Throwable caught) {
                    display.setLoading(false);
                    GWT.log("ERROR while deleting", caught);
                    eventBus.fireEvent(new FlashEvent(messages.deleteChannelFailed(channel.getTitle()), -1));
                }

                public void onSuccess(RemoveChannelResult result) {
                    display.setLoading(false);
                    eventBus.fireEvent(new FlashEvent(messages.channelDeleted(channel.getTitle()), 2000));
                    display.deleteSelectedChannel();
                }

            });

        }

    }

    private List<TreeItem> createTreeNodes(List<ChannelModel> list) {
        List<TreeItem> items = new ArrayList<TreeItem>();

        TreeItem parent = null;
        TreeItem last = null;

        for (ChannelModel m : list) {

            TreeItem item = createChannelTreeItem(m);

            if (null == m.getParent()) {
                items.add(item);
            }
            if (null == parent) {
                parent = item;
            } else if (isParent(parent, m)) {
                parent.addItem(item);
            } else if (isParent(last, m)) {
                last.addItem(item);
                parent = last;
            } else {
                do {
                    if (isParent(parent, m)) {
                        parent.addItem(item);
                        break;
                    }
                    parent = parent.getParentItem();
                } while (null != parent);

            }
            last = item;
        }
        return items;
    }

    protected void loadTreeItems() {
        GWT.log("loadTreeItems");
        display.setLoading(true);
        dispatcher.execute(new FetchChannels(), new AsyncCallback<FetchChannelsResult>() {
            @Override
            public void onFailure(Throwable caught) {
                GWT.log("ERROR while fetching channels", caught);
                display.setLoading(false);

            }

            @Override
            public void onSuccess(FetchChannelsResult result) {
                display.setAddEnabled(false);
                display.setEditEnabled(false);
                display.setDeleteEnabled(false);

                display.bindTreeItems(createTreeNodes(result.get()));

                display.setLoading(false);
            }

        });
    }

    private TreeItem createChannelTreeItem(ChannelModel m) {
        if (null == m.getParent()) {
            return createTreeItem(m.getTitle(), resources.world(), m);
        } else {
            return createTreeItem(m.getTitle(), resources.folder(), m);
        }

    }

    private String imageItemHTML(String title, ImageResource imageProto) {
        return AbstractImagePrototype.create(imageProto).getHTML() + " " + title;
    }

    private TreeItem createTreeItem(String title, ImageResource imageProto, Object userObj) {
        TreeItem item = new TreeItem(imageItemHTML(title, imageProto));
        item.setUserObject(userObj);
        return item;
    }

    private boolean isParent(TreeItem parent, ChannelModel test) {
        ChannelModel p = (ChannelModel) parent.getUserObject();
        return p.getId().equals(test.getParent());
    }

}
